/****************************************
 * 2018 - 2021 版权所有 CopyRight(c) 快程乐码信息科技有限公司所有, 未经授权，不得复制、转发
 */

package com.kclm.security.demo.security;

import com.kclm.security.demo.service.impl.UserServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/******************
 * @Author yejf
 * @Version v1.0
 * @Create 2020-07-19 22:48
 * @Description SpringSecurity配置类
 */
@Configuration
@Slf4j
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserServiceImpl userService;

    public SpringSecurityConfig() {
        //
        log.debug("开始创建SpringSecurityConfig实例...");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        //
        log.debug("创建 PasswordEncoder的Bean");
        return new BCryptPasswordEncoder();
    }

    /************
     * 认证方法
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //
        log.debug("进入configure(AuthenticationManagerBuilder)方法，开始认证的操作");
        //内存预定义对象，可选，由于spring security5 必需要对密码进行加密，所以我们使用{noop}来显示地使用明文密码
        //auth.inMemoryAuthentication().withUser("jack").password("{noop}1234").roles("USER");
        //auth.inMemoryAuthentication().withUser("solo").password("{noop}123456").roles("ADMIN","USER");
        //如果不使用 {noop}，则指定加密方式
        auth.inMemoryAuthentication().withUser("jack").password(passwordEncoder().encode("1234")).roles("USER");
        auth.inMemoryAuthentication().withUser("solo").password(passwordEncoder().encode("123456")).roles("ADMIN","USER");
        //设置认证方法： 从数据库中加载用户进行认证
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        log.debug("--进入configure(WebSecurity)方法，可以在此指定通过一些静态资源");

    }

    /****************
     * 授权方法
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        log.debug("进入授权的configure(HttpSecurity)方法，开始授权的相关操作");
        //
        http.csrf().disable(); //阻塞CSRF攻击
        //
        http.authorizeRequests()
                .antMatchers("/login","/static/**").permitAll()
                //要访问 home 资源，必需要有 USER 角色权限
                .antMatchers("/", "/home").hasRole("USER")
                //要访问 /admin 资源，必需要有 ADMIN 或 DBA 角色中的任一一个
                .antMatchers("/admin/**").hasAnyRole("ADMIN","DBA")
                .anyRequest().authenticated()
                .and()
                //设置登录 页面操作
                .formLogin()
                .loginPage("/login")
                //登录成功后的处理接口
                //.loginProcessingUrl("/home")
                .usernameParameter("username").passwordParameter("password")
                //.defaultSuccessUrl("/main")  //登录成功默认去到的目的地url
                //指定使用登录成功的处理器来处理进入不同的资源
                .successHandler(new LoginSuccessHandler())
                .failureUrl("/login?error")
                .and()
                //设置 注销操作, 默认的 logoutUrl就是 /logout
                .logout().logoutUrl("/logout")
                //指定注销成功后的 回调处理
                /*.logoutSuccessHandler((request, response, authentication) -> {
                    log.debug("注销成功");
                    response.setContentType("application/json;charset=utf-8");
                    final PrintWriter writer = response.getWriter();
                    writer.write("注销成功");
                    writer.flush();
                })*/
                .permitAll()
                .and()
                //指定异常处理页面
                .exceptionHandling().accessDeniedPage("/accessDenied");
    }
}
